package com.hexacta.hrs.web.struts.actions.mentoring;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.beanutils.BeanPropertyValueEqualsPredicate;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.struts2.interceptor.SessionAware;
import org.vectrics.common.util.DateUtil;
import org.vectrics.employee.Employee;
import org.vectrics.hibernate.ThreadProperties;
import org.vectrics.recruiter.RecruitServiceLocator;

import com.hexacta.hrs.domain.mentoring.Action;
import com.hexacta.hrs.domain.mentoring.Meeting;
import com.hexacta.hrs.domain.mentoring.MeetingStatus;
import com.hexacta.hrs.domain.mentoring.PriorityTypes;
import com.hexacta.hrs.domain.mentoring.Theme;
import com.hexacta.hrs.domain.mentoring.ThemeTypes;
import com.hexacta.hrs.securitygroups.PermissionName;
import com.hexacta.hrs.services.crud.CrudService;
import com.hexacta.hrs.services.employee.EmployeeService;
import com.hexacta.hrs.services.mentoring.MeetingService;
import com.hexacta.hrs.util.MentoringUtil;
import com.hexacta.hrs.web.struts.actions.CrudAbstractAction;

/**
 * Action de Struts para {@link Meetings}.
 * 
 * @author vschimpf, ncortinez
 */
public class MeetingAction extends CrudAbstractAction<Meeting> implements SessionAware {

    private static final long serialVersionUID = -7604226709020781733L;

    /*
     * PROPERTIES
     */

    private SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy - hh:mm a");

    private static String YES = "Yes";

    private static String NO = "No";

    private static String AM = "AM";

    private static String PM = "PM";

    private Map session;

    private MeetingService meetingService;

    private EmployeeService employeeService;

    private String selectedMentorId;

    private String selectedMentorName;

    private String selectedEmployeeId;

    private String selectedEmployeeName;

    private Date meetingDateFrom;

    private Date meetingDateTo;

    private Date lastMeetingDate;

    private Date nextMeetingDate;

    private String nextMeetingMessage;

    private Date selectedMeetingDate;

    private String meetingDate;

    private String selectedStatus;

    private Boolean delayed;

    private Boolean checkerByHR;

    private String checkerHRName;

    private String newAction;

    private String newTheme;

    private String selectedActionForDelete;

    private String selectedThemeForDelete;

    private String newTopic;

    private String selectedPriorityType;

    private String selectedTopicType;

    private Boolean isEditing;

    private Integer selectedHour;

    private Integer selectedMinutes;

    private String selectedMeridian;

    private Integer maxPeriodBetweenMeetings;

    private Boolean maxTimeNextMeetingError = Boolean.FALSE;

    private Boolean maxActualTimeError = Boolean.FALSE;

    private String recommendedDate;

    private Boolean validated = Boolean.FALSE;

    private Integer delay;

    private Boolean onlyCurrent;

    private String stopRefresh;

    private boolean notCheckedByHRPermission;

    /*
     * GETTERS AND SETTERS
     */

    public void setMeetingService(final MeetingService meetingService) {
        this.meetingService = meetingService;
    }

    public void setEmployeeService(final EmployeeService employeeService) {
        this.employeeService = employeeService;
    }

    public Date getMeetingDateFrom() {
        return meetingDateFrom;
    }

    public void setMeetingDateFrom(final Date meetingDate) {
        meetingDateFrom = meetingDate;
    }

    public Date getMeetingDateTo() {
        return meetingDateTo;
    }

    public void setMeetingDateTo(final Date meetingDate) {
        meetingDateTo = meetingDate;
    }

    public String getSelectedMentorId() {
        return selectedMentorId;
    }

    public void setSelectedMentorId(final String selectedMentorId) {
        this.selectedMentorId = selectedMentorId;
    }

    public String getSelectedMentorName() {
        return selectedMentorName;
    }

    public void setSelectedMentorName(final String selectedMentorName) {
        this.selectedMentorName = selectedMentorName;
    }

    public String getSelectedEmployeeId() {
        return selectedEmployeeId;
    }

    public void setSelectedEmployeeId(final String selectedEmployeeId) {
        this.selectedEmployeeId = selectedEmployeeId;
    }

    public String getSelectedEmployeeName() {
        return selectedEmployeeName;
    }

    public void setSelectedEmployeeName(final String selectedEmployeeName) {
        this.selectedEmployeeName = selectedEmployeeName;
    }

    public String getSelectedStatus() {
        return selectedStatus;
    }

    public void setSelectedStatus(final String meetingStatus) {
        selectedStatus = meetingStatus;
    }

    public Boolean getDelayed() {
        return delayed;
    }

    public void setDelayed(final Boolean delayed) {
        this.delayed = delayed;
    }

    public String getCheckerHRName() {
        return checkerHRName;
    }

    public void setCheckerHRName(final String checketHRName) {
        checkerHRName = checketHRName;
    }

    public Date getLastMeetingDate() {
        return lastMeetingDate;
    }

    public void setLastMeetingDate(final Date lastMeetingDate) {
        this.lastMeetingDate = lastMeetingDate;
    }

    public Date getNextMeetingDate() {
        return nextMeetingDate;
    }

    public void setNextMeetingDate(final Date nextMeetingDate) {
        this.nextMeetingDate = nextMeetingDate;
    }

    public void setNextMeetingMessage(final String nextMeetingMessage) {
        this.nextMeetingMessage = nextMeetingMessage;
    }

    public String getNextMeetingMessage() {
        return nextMeetingMessage;
    }

    public Boolean getCheckerByHR() {
        return checkerByHR;
    }

    public void setCheckerByHR(final Boolean checkerByHR) {
        this.checkerByHR = checkerByHR;
    }

    public void setMaxTimeNextMeetingError(final Boolean maxTimeNextMeetingError) {
        this.maxTimeNextMeetingError = maxTimeNextMeetingError;
    }

    public Boolean getMaxTimeNextMeetingError() {
        return maxTimeNextMeetingError;
    }

    public void setMaxActualTimeError(final Boolean maxActualTimeError) {
        this.maxActualTimeError = maxActualTimeError;
    }

    public Boolean getMaxActualTimeError() {
        return maxActualTimeError;
    }

    public void setSelectedMeetingDate(final Date selectedMeetingDate) {
        this.selectedMeetingDate = selectedMeetingDate;
    }

    public Date getSelectedMeetingDate() {
        return selectedMeetingDate;
    }

    @Override
    protected CrudService<Meeting> getCrudService() {
        return meetingService;
    }

    public Collection<Employee> getMentorList() {
        return employeeService.getEmployeesOnly();
    }

    public List<MeetingStatus> getAllStatuses() {
        return MeetingStatus.getAllMeetingStatus();
    }

    public Map getSession() {
        return session;
    }

    public void setSession(final Map session) {
        this.session = session;
    }

    public String getNewTopic() {
        return newTopic;
    }

    public void setNewTopic(final String newTopic) {
        this.newTopic = newTopic;
    }

    public String getSelectedPriorityType() {
        return selectedPriorityType;
    }

    public void setSelectedPriorityType(final String selectedPriorityType) {
        this.selectedPriorityType = selectedPriorityType;
    }

    public String getSelectedTopicType() {
        return selectedTopicType;
    }

    public void setSelectedTopicType(final String selectedTopicType) {
        this.selectedTopicType = selectedTopicType;
    }

    public String getNewAction() {
        return newAction;
    }

    public void setNewAction(final String newAction) {
        this.newAction = newAction;
    }

    public void setMaxPeriodBetweenMeetings(final Integer maxPeriodBetweenMeetings) {
        this.maxPeriodBetweenMeetings = maxPeriodBetweenMeetings;
    }

    public Integer getMaxPeriodBetweenMeetings() {
        return maxPeriodBetweenMeetings;
    }

    public void setSelectedActionForDelete(final String selectedActionForDelete) {
        this.selectedActionForDelete = selectedActionForDelete;
    }

    public String getSelectedActionForDelete() {
        return selectedActionForDelete;
    }

    public void setSelectedThemeForDelete(final String selectedThemeForDelete) {
        this.selectedThemeForDelete = selectedThemeForDelete;
    }

    public String getSelectedThemeForDelete() {
        return selectedThemeForDelete;
    }

    public void setNewTheme(final String newTheme) {
        this.newTheme = newTheme;
    }

    public String getNewTheme() {
        return newTheme;
    }

    public List<String> getCheckerByHROptions() {
        List<String> checkerOptions = new ArrayList<String>();
        checkerOptions.add(MeetingAction.YES);
        checkerOptions.add(MeetingAction.NO);

        return checkerOptions;
    }

    public boolean getIsEditing() {
        return isEditing;
    }

    public String getMeetingDate() {
        return meetingDate;
    }

    public void setMeetingDate(final String meetingDate) {
        this.meetingDate = meetingDate;
    }

    public void setOnlyCurrent(final Boolean onlyCurrent) {
        this.onlyCurrent = onlyCurrent;
    }

    public Boolean getOnlyCurrent() {
        return onlyCurrent != null ? onlyCurrent : Boolean.TRUE;
    }

    public Integer getSelectedHour() {
        return selectedHour;
    }

    public void setSelectedHour(final Integer selectedHour) {
        this.selectedHour = selectedHour;
    }

    public void setNotCheckedByHRPermission(final boolean notCheckedByHRPermission) {
        this.notCheckedByHRPermission = notCheckedByHRPermission;
    }

    public boolean getNotCheckedByHRPermission() {
        return !hasPermission(PermissionName.MEETING_CHECKED_BYHR.toString());
    }

    public Integer getSelectedMinutes() {
        return selectedMinutes;
    }

    public void setSelectedMinutes(final Integer selectedMinutes) {
        this.selectedMinutes = selectedMinutes;
    }

    public String getSelectedMeridian() {
        return selectedMeridian;
    }

    public void setSelectedMeridian(final String selectedMeridian) {
        this.selectedMeridian = selectedMeridian;
    }

    public Collection<Integer> getHoursList() {
        return DateUtil.getHoursList(true);
    }

    public Collection<Integer> getMinutesList() {
        return DateUtil.getMinutesList();
    }

    public Collection<String> getMeridianList() {
        Collection<String> meridian = new ArrayList<String>();
        meridian.add(AM);
        meridian.add(PM);
        return meridian;
    }

    public String getRecommendedDate() {
        return recommendedDate;
    }

    public void setRecommendedDate(final String recommendedDate) {
        this.recommendedDate = recommendedDate;
    }

    public Boolean getValidated() {
        return validated;
    }

    public void setValidated(final Boolean validated) {
        this.validated = validated;
    }

    public Integer getDelay() {
        return delay;
    }

    public void setDelay(final Integer delayedDays) {
        delay = delayedDays;
    }

    public Meeting getMeeting() {
        return getModel();
    }

    public void setStopRefresh(final String stopRefresh) {
        this.stopRefresh = stopRefresh;
    }

    public String getStopRefresh() {
        return stopRefresh == null ? "false" : stopRefresh;
    }

    /*
     * COMMON METHODS
     */

    @Override
    protected Meeting getSearchCriteria() {
        Meeting criteria = new Meeting();

        // Filtro por Employee
        if (!StringUtils.isEmpty(selectedEmployeeName)) {
            long id = NumberUtils.toLong(selectedEmployeeId, Long.MIN_VALUE);
            if (Long.MIN_VALUE != id) {
                criteria.setEmployee(employeeService.searchById(id));
            } else {
                // Para que no traiga nada
                criteria.setEmployee(new Employee());
            }
        }

        // Filtro por Mentor
        if (!StringUtils.isEmpty(selectedMentorName)) {
            long id = NumberUtils.toLong(selectedMentorId, Long.MIN_VALUE);
            if (Long.MIN_VALUE != id) {
                criteria.setMentor(employeeService.searchById(id));
            } else {
                // Para que no traiga nada
                criteria.setEmployee(new Employee());
            }
        }

        // Filtro por la reuni�n planificada
        criteria.setStatus(MeetingStatus.SCHEDULED);

        return criteria;
    }

    /**
     * Crea un mapa con todos los valores del formulario como par�metros
     * 
     * @return
     */
    protected Map<String, Object> getParameters() {
        Map<String, Object> parameters = new HashMap<String, Object>();

        // Filtro por MeetingStatus
        if (!StringUtils.isEmpty(selectedStatus)) {
            try {
                parameters.put("status", MeetingStatus.valueOf(selectedStatus));
            } catch (Exception e) {
            }
        }

        // Filtro por Employee
        if (!StringUtils.isEmpty(selectedEmployeeName)) {
            long id = NumberUtils.toLong(selectedEmployeeId, Long.MIN_VALUE);
            if (Long.MIN_VALUE != id) {
                parameters.put("employee", employeeService.searchById(id));
            } else {
            }
        }

        // Filtro por Mentor
        if (!StringUtils.isEmpty(selectedMentorName)) {
            long id = NumberUtils.toLong(selectedMentorId, Long.MIN_VALUE);
            if (Long.MIN_VALUE != id) {
                parameters.put("mentor", employeeService.searchById(id));
            }
        } else {
            if (!hasPermission(PermissionName.LIST_ALL_MENTORS.toString())) {
                // Si no tengo permiso para ver todos los mentores, solo muestro
                // al
                // usuario actual como mentor.
                Long companyId = ThreadProperties.getDomain().getPrimaryCompany().getId();
                Employee mentor = RecruitServiceLocator.getCompanyService().findEmployee(getAuthPerson().getId(),
                        companyId);

                parameters.put("mentor", mentor);
            }
        }

        // Filtro por fecha desde
        if (meetingDateFrom != null) {
            parameters.put("dateFrom", meetingDateFrom);
        }

        // Filtro por fecha hasta
        if (meetingDateTo != null) {
            parameters.put("dateTo", meetingDateTo);
        }

        // Filtro por checker
        if (!StringUtils.isEmpty(checkerHRName)) {
            parameters.put("checkedByHR", (checkerHRName.equals(MeetingAction.YES) ? Boolean.TRUE : Boolean.FALSE));
        }

        // Filtro por delayed
        if (delayed != null && delayed.equals(Boolean.TRUE)) {
            parameters.put("onlyDelayed", delayed);
        }

        // Filtro por onlyCurrentRelationships
        parameters.put("onlyCurrentRelationships", getOnlyCurrent());

        return parameters;
    }

    /**
     * This method should be override in order to call SearchByCriteria or
     * {@inheritDoc}
     */
    @Override
    protected Collection<Meeting> loadListModel() {
        getSession().remove("meeting");
        validateList();
        return meetingService.searchMeetings(getParameters());
    }

    public List<ThemeTypes> getTopicTypes() {
        return ThemeTypes.getAllThemeTypes();
    }

    public List<PriorityTypes> getPriorityTypes() {
        return PriorityTypes.getAllPriorityTypes();
    }

    /*
     * Limpio todas las propiedades
     */
    private void clearProperties() {
        setSelectedEmployeeName(null);
        setSelectedEmployeeId(null);
        setSelectedMentorName(null);
        setSelectedMentorId(null);
        setMeetingDateFrom(null);
        setMeetingDateTo(null);
        setSelectedStatus(null);
        setCheckerHRName(null);
        setCheckerByHR(null);
        setDelayed(null);
        setNextMeetingDate(null);
        setSelectedMeetingDate(null);
        setLastMeetingDate(null);
        setSelectedHour(null);
        setSelectedMinutes(null);
        setSelectedMeridian(null);
        setNewTopic(null);
        setNewAction(null);
        setSelectedPriorityType(null);
        setSelectedTopicType(null);
        setDelay(null);
        setModel(null);
        isEditing = null;
    }

    /*
     * Asigno los valores de la meeting a todas las propiedades
     */
    private void setProperties(final Meeting meeting) {
        // Es necesario obtener los empleados de la base para obtener sus
        // propiedades sin problemas ya que el mentor y mentorado de una meeting
        // no van a cambiar
        meeting.setEmployee(employeeService.searchById(meeting.getEmployee().getId()));
        meeting.setMentor(employeeService.searchById(meeting.getMentor().getId()));
        setSelectedEmployeeId(meeting.getEmployee().getId().toString());
        setSelectedEmployeeName(meeting.getEmployee().getPerson().getName());
        setSelectedMentorId(meeting.getMentor().getId().toString());
        setSelectedMentorName(meeting.getMentor().getPerson().getName());

        // date
        setSelectedMeetingDate(meeting.getDate());
        setMeetingDateFrom(meeting.getDate());
        setMeetingDateTo(meeting.getDate());
        setLastMeetingDate(meeting.getPreviousMeeting() == null ? null : meeting.getPreviousMeeting().getDate());
        Meeting nextMeeting = meetingService.findNextMeeting(meeting);
        setNextMeetingDate(nextMeeting == null ? null : nextMeeting.getDate());
        setMeetingDate(sdf.format(meeting.getDate()));
        setCompleteDate(meeting.getDate());

        // delay
        Integer delayDays = 0;// Calculamos los dias de demora
        if (meeting.getPreviousMeeting() != null) {
            meeting.calculateDelayedDays(
                    getHolidaysFromService(meeting.getPreviousMeeting().getDate(), meeting.getDate()),
                    getMaxPeriodBetweenMeetings());
            delayDays = meeting.getDelayedDays();
        }
        setDelay(delayDays);
        if (getSession().containsKey("editMode")) {
            isEditing = (Boolean) getSession().get("editMode");
        }

        // status
        setSelectedStatus(meeting.getStatus().getName());

        // checked by hr
        setCheckerByHR(meeting.getCheckedByHR());
        setCheckerHRName(meeting.getCheckedByHR() != null && meeting.getCheckedByHR().equals(Boolean.TRUE) ? MeetingAction.YES
                : MeetingAction.NO);

        // request id
        setRequestId(meeting.getId());

        // OnlyCurrentRelationship
        setOnlyCurrent(meeting.getMentor().getId().equals(meeting.getEmployee().getMentor().getId()));

        // model
        setModel(meeting);
    }

    /*
     * Este m�todo obtiene la lista de feriados desde el webservice
     */
    private List<Date> getHolidaysFromService(final Date fromDate, final Date toDate) {
        List<Date> holidays = new ArrayList<Date>();
        try {
            holidays = meetingService.getHolidays(fromDate, toDate);
        } catch (RuntimeException e) {
        }

        return holidays;
    }

    /*
     * Este m�todo crea una nueva instancia de meeting a partir de una ya
     * existente, para usar como DTO.
     */
    private Meeting createMeetingDTO(final Meeting meetingObject) {
        if (meetingObject == null)
            return null;

        // Copy meeting (DTO)
        Meeting meetingDto = meetingObject.copyExceptPreviousMeeting();

        if (meetingObject.getPreviousMeeting() != null) {
            // Copy previous meeting
            Meeting previousMeetingDto = new Meeting();
            previousMeetingDto.setId(meetingObject.getPreviousMeeting().getId());
            previousMeetingDto.setDate(meetingObject.getPreviousMeeting().getDate());
            meetingDto.setPreviousMeeting(previousMeetingDto);
        }
        return meetingDto;
    }

    /*
     * MEETINGS
     */

    /**
     * Plan Meeting (Add)
     */
    public String addMeeting() {
        setValidated(Boolean.FALSE);

        // Si estan seleccionados el mentor y el mentorado seteo los datos.
        if (StringUtils.isNotEmpty(getSelectedMentorName()) && StringUtils.isNotEmpty(getSelectedEmployeeName())) {

            if (!actualMentoringRelationship()) {
                setModel(null);
                setMappedRequest("addMeeting");
                return SUCCESS;
            }

            Meeting selectedMeeting = new Meeting();
            selectedMeeting.setEmployee(employeeService.searchById(NumberUtils.toLong(getSelectedEmployeeId(),
                    Long.MIN_VALUE)));
            selectedMeeting.setMentor(employeeService.searchById(NumberUtils.toLong(getSelectedMentorId(),
                    Long.MIN_VALUE)));

            Calendar current = DateUtil.getCurrentCalendar();
            current.set(Calendar.DAY_OF_YEAR, current.get(Calendar.DAY_OF_YEAR) + 1);
            selectedMeeting.setDate(current.getTime());

            // Seteo la fecha de la �ltima reunion.
            Meeting lastMeeting = meetingService.findPreviousMeeting(selectedMeeting);
            if (lastMeeting != null) {
                setLastMeetingDate(lastMeeting.getDate());
            } else {
                setLastMeetingDate(null);
            }

            // Seteo la fecha de la pr�xima reunion.
            Meeting scheduledMeeting = meetingService.findScheduledMeeting(selectedMeeting);
            if (scheduledMeeting != null) {
                setNextMeetingDate(scheduledMeeting.getDate());
                setCompleteDate(scheduledMeeting.getDate());
                setNextMeetingMessage("this date belongs to a previously scheduled meeting.");
            } else {
                setNextMeetingMessage("a new meeting will be scheduled for this date.");
                Calendar c = new GregorianCalendar();
                c.setTime(selectedMeeting.getDate());

                // si el dia es mayor a 28, lo convierto en 28 para evitar
                // errores del tipo: 30/02 no es fecha valida.
                if (c.get(Calendar.DAY_OF_MONTH) > 28) {
                    c.set(Calendar.DAY_OF_MONTH, 28);
                }
                c.add(Calendar.MONTH, 4);

                // Calculo 10 d�as antes del fin del lapso m�ximo entre meetings
                setNextMeetingDate(DateUtil.calculateNextDate(selectedMeeting.getDate(),
                        getMaxPeriodBetweenMeetings() - 10,
                        getHolidaysFromService(selectedMeeting.getDate(), c.getTime())));
                setCompleteDate(selectedMeeting.getDate());
            }
        }

        setMappedRequest("addMeeting");
        return SUCCESS;
    }

    public String resetAddMeeting() {
        clearProperties();
        setStopRefresh(null);
        setMappedRequest("addMeeting");
        return SUCCESS;
    }

    /**
     * Guarda la nueva meeting
     */
    public String addNewMeeting() {
        // Si ya habia una reunion planificada, actualizo la fecha. Si no
        // planifico una reunion para la fecha indicada.
        Meeting meetingSelected = new Meeting();
        meetingSelected.setEmployee(employeeService.searchById(NumberUtils.toLong(getSelectedEmployeeId(),
                Long.MIN_VALUE)));
        meetingSelected
                .setMentor(employeeService.searchById(NumberUtils.toLong(getSelectedMentorId(), Long.MIN_VALUE)));

        Meeting scheduledMeeting = meetingService.findScheduledMeeting(meetingSelected);
        if (scheduledMeeting != null) {
            setModel(scheduledMeeting);
        }

        doActionsBeforeSave();
        return super.save();
    }

    /*
     * Este m�todo obtiene una fecha a partir de los campos fecha, hora, minutos
     * y meridiano
     */
    private Date getCompleteDate(final Date selectedDate) {
        Calendar c = new GregorianCalendar();
        c.setTime(selectedDate);
        c.set(Calendar.AM_PM, PM.equals(getSelectedMeridian()) ? Calendar.PM : Calendar.AM);
        if (getSelectedHour() == 12) {
            c.set(Calendar.HOUR, 0);
        } else {
            c.set(Calendar.HOUR, getSelectedHour());
        }
        c.set(Calendar.MINUTE, getSelectedMinutes());
        return c.getTime();
    }

    /*
     * Este m�todo completa los campos relacionados a la fecha
     */
    private void setCompleteDate(final Date date) {
        Calendar c = new GregorianCalendar();
        c.setTime(date);
        setSelectedHour(c.get(Calendar.HOUR) == 0 ? 12 : c.get(Calendar.HOUR));
        setSelectedMinutes(c.get(Calendar.MINUTE));
        setSelectedMeridian(c.get(Calendar.AM_PM) == Calendar.PM ? PM : AM);
    }

    /*
     * Save/Update Meeting
     */

    /**
     * Acciones antes de guardar
     */
    @Override
    protected void doActionsBeforeSave() {
        Meeting model = getModel();
        if (model.getEmployee() == null) {
            model.setEmployee(employeeService.searchById(NumberUtils.toLong(getSelectedEmployeeId(), Long.MIN_VALUE)));
        }
        if (model.getMentor() == null) {
            model.setMentor(employeeService.searchById(NumberUtils.toLong(getSelectedMentorId(), Long.MIN_VALUE)));
        }
        model.setStatus(MeetingStatus.SCHEDULED);
        getModel().setDate(getCompleteDate(getNextMeetingDate()));
        model.setCheckedByHR(getCheckerByHR());
    }

    /**
     * Acciones despues de guardar
     */
    @Override
    protected void doActionsAfterSave() {
        setProperties(getModel());
    }

    /*
     * Delete Meeting
     */

    /**
     * acciones despues de eliminar
     */
    @Override
    protected void doActionsAfterDelete() {
        clearProperties();
    }

    /**
     * View Meeting
     */
    public String showDetails() {
        Meeting m = getCrudService().searchById(getRequestId());
        setProperties(createMeetingDTO(m));

        setMappedRequest("showDetails");
        return SUCCESS;
    }

    /**
     * Edit Meeting
     */
    public String editMeeting() {
        setValidated(Boolean.FALSE);

        Meeting m = getCrudService().searchById(getRequestId());

        // Refresh de valores
        setProperties(createMeetingDTO(m));

        // Guardo el modo en la sesion.
        isEditing = Boolean.TRUE;
        getSession().put("editMode", isEditing);

        // Guardo la meeting en la sesion.
        getSession().put("meeting", getModel());

        setMappedRequest("editMeeting");
        return SUCCESS;
    }

    /**
     * Guarda la meeting editada
     */
    public String saveEditedMeeting() {
        // Obtengo la Meeting por la sesion.
        Meeting modifiedMeeting = (Meeting) getSession().get("meeting");
        Meeting synchMeeting = synchronizeMeeting(modifiedMeeting);
        synchMeeting.setCheckedByHR(getCheckerByHR());
        synchMeeting.setDate(getCompleteDate(getSelectedMeetingDate()));
        setModel(synchMeeting);

        getSession().remove("meeting");
        getSession().remove("editMode");
        getCrudService().save(getModel());
        doActionsAfterSave();

        return list();
    }

    /*
     * Sincroniza la meeting de la base con la modificada en memoria para poder
     * persistirla
     */
    private Meeting synchronizeMeeting(final Meeting modifiedMeeting) {
        Meeting synchMeeting = getCrudService().searchById(modifiedMeeting.getId());

        // guardamos fecha y check
        synchMeeting.setDate(modifiedMeeting.getDate());
        synchMeeting.setCheckedByHR(modifiedMeeting.getCheckedByHR());

        // Obtener temas ya persistidos
        Set<Theme> oldThemes = new HashSet<Theme>(synchMeeting.getNoteThemes());
        synchMeeting.getNoteThemes().clear();
        for (Object element : modifiedMeeting.getNoteThemes()) {
            Theme theme = (Theme) element;
            Theme oldTheme = (Theme) MentoringUtil.findObjectById(oldThemes, theme);
            if (oldTheme != null) {
                synchMeeting.getNoteThemes().add(oldTheme);
            } else {
                synchMeeting.getNoteThemes().add(theme);
            }
        }

        // Obtener acciones ya persistidas
        Set<Action> oldActions = new HashSet<Action>(synchMeeting.getActionsToDo());
        synchMeeting.getActionsToDo().clear();
        for (Object element : modifiedMeeting.getActionsToDo()) {
            Action action = (Action) element;
            Action oldAction = (Action) MentoringUtil.findObjectById(oldActions, action);
            if (oldAction != null) {
                synchMeeting.getActionsToDo().add(oldAction);
            } else {
                synchMeeting.getActionsToDo().add(action);
            }
        }

        return synchMeeting;
    }

    /**
     * Este m�todo indica si la meeting ha sido realizada
     */
    public boolean getIsPerformed() {
        return getModel() != null && MeetingStatus.PERFORMED.equals(getModel().getStatus());
    }

    /**
     * Este m�todo indica si el usuario tiene o no permisos de RRHH
     */
    public boolean getIsNotRRHH() {
        return false;// TODO: seguridad
    }

    /*
     * CONFIRM PERFORMED MEETINGS
     */

    /**
     * Carga el formulario de busqueda de meetings planificadas.
     * 
     * @return
     */
    public String searchPerformedMeeting() {
        clearProperties();
        setMappedRequest("searchPerformedMeeting");
        return SUCCESS;
    }

    /**
     * Busca la meeting con estado schedule de la relacion mentor-mentorado.
     * 
     * @return
     */
    public String listPerformedMeeting() {
        // Si no estan seleccionados el mentor y el mentorado limpio los campos
        if (StringUtils.isEmpty(getSelectedMentorName()) && StringUtils.isEmpty(getSelectedEmployeeName())) {
            clearProperties();
        }

        getSession().remove("editMode");

        if (getSelectedEmployeeId() != null && getSelectedMentorId() != null && !actualMentoringRelationship()) {
            setModel(null);
            setMappedRequest("searchPerformedMeeting");
            return ERROR;
        }

        // busco la meeting seg�n el criterio.
        Collection<Meeting> meetings = getCrudService().searchByCriteria(getSearchCriteria());

        // Si esta vacio, cancelo y muestro un mensaje de error.
        if (meetings.isEmpty()) {
            addFieldError("mentor and mentee", "There is no planned meeting between the mentor and the mentee");
            searchPerformedMeeting();
            return ERROR;
        }

        if (meetings != null && !meetings.isEmpty()) {
            // Cargo como meeting la primera y �nica de la coleccion.
            Meeting meeting = meetings.iterator().next();
            setModel(meeting);
        } else {
            setModel(null);
        }

        setModel(createMeetingDTO(getModel()));

        // Asignamos los campos relacionados a la fecha
        setCompleteDate(getModel().getDate());

        // Guardo la meeting en la sesion.
        getSession().put("meeting", getModel());

        setMappedRequest("listPerformedMeeting");
        return SUCCESS;
    }

    /**
     * Graba una meeting como realizada con la fecha, temas y actividades
     * indicadas.
     * 
     * @return
     */
    public String confirmPerformedMeeting() {

        // Obtengo la Meeting por la sesion.
        Meeting modifiedMeeting = (Meeting) getSession().get("meeting");
        Meeting synchMeeting = synchronizeMeeting(modifiedMeeting);
        synchMeeting.setDate(getCompleteDate(getSelectedMeetingDate()));
        synchMeeting.setStatus(MeetingStatus.PERFORMED);
        setModel(synchMeeting);

        // set dates for add meeting page
        setLastMeetingDate(synchMeeting.getDate());
        // Calculo 10 d�as antes del fin del lapso m�ximo entre meetings
        Calendar c = new GregorianCalendar();
        c.setTime(synchMeeting.getDate());
        c.add(Calendar.MONTH, 4);
        setNextMeetingDate(DateUtil.calculateNextDate(synchMeeting.getDate(), getMaxPeriodBetweenMeetings() - 10,
                getHolidaysFromService(synchMeeting.getDate(), c.getTime())));
        setCompleteDate(getNextMeetingDate());

        // guardo
        getCrudService().save(getModel());

        setMappedRequest("confirmPerformedMeeting");
        return SUCCESS;
    }

    /**
     * Agrega un Tema a la meeting
     * 
     * @return
     */
    public String addTheme() {
        // Obtengo la Meeting por la sesion.
        Meeting meet = (Meeting) getSession().get("meeting");
        setModel(meet);

        // Guardo la fecha
        if (getSelectedMeetingDate() != null) {
            if (getSelectedHour() != null && getSelectedMinutes() != null && getSelectedMeridian() != null) {
                getModel().setDate(getCompleteDate(getSelectedMeetingDate()));
            } else {
                getModel().setDate(getSelectedMeetingDate());
            }
        }
        // Guardo la opcion CheckedByHR
        if (getCheckerByHR() != null) {
            getModel().setCheckedByHR(getCheckerByHR());
        }

        // Guardo el tema si se llenaron todos los campos.
        if (StringUtils.isNotEmpty(getNewTopic()) && StringUtils.isNotEmpty(getSelectedTopicType())
                && StringUtils.isNotEmpty(getSelectedPriorityType())) {
            // Agrego el tema.
            Theme theme = new Theme();
            theme.setName(StringEscapeUtils.escapeHtml(getNewTopic()));
            theme.setPriority(PriorityTypes.valueOf(getSelectedPriorityType()));
            theme.setType(ThemeTypes.valueOf(getSelectedTopicType()));

            getModel().getNoteThemes().add(theme);

        }

        // Guardo la meeting en la session.
        getSession().put("meeting", getModel());

        // Seteo los campos del formulario.
        setFormThemesAndActions();

        return SUCCESS;
    }

    /**
     * Elimina un tema de la meeting
     * 
     * @return
     */
    public String deleteTheme() {
        // Obtengo la Meeting por la sesion.
        Meeting meet = (Meeting) getSession().get("meeting");
        setModel(meet);

        // Obtengo la accion y la elimino de la lista.
        Theme themeForDelete = (Theme) CollectionUtils.find(getModel().getNoteThemes(),
                new BeanPropertyValueEqualsPredicate("name", getSelectedThemeForDelete()));
        getModel().getNoteThemes().remove(themeForDelete);

        // Guardo la fecha
        if (getSelectedMeetingDate() != null) {
            getModel().setDate(getCompleteDate(getSelectedMeetingDate()));
        }
        // Guardo la opcion CheckedByHR
        if (getCheckerByHR() != null) {
            getModel().setCheckedByHR(getCheckerByHR());
        }

        // Guardo la meeting en la session.
        getSession().put("meeting", getModel());

        setFormThemesAndActions();

        return SUCCESS;
    }

    /**
     * Agrega un action a la meeting
     * 
     * @return
     */
    public String addAction() {
        // Obtengo la Meeting por la sesion.
        Meeting meet = (Meeting) getSession().get("meeting");
        setModel(meet);

        // Guardo la fecha
        if (getSelectedMeetingDate() != null) {
            getModel().setDate(getCompleteDate(getSelectedMeetingDate()));
        }
        // Guardo la opcion CheckedByHR
        if (getCheckerByHR() != null) {
            getModel().setCheckedByHR(getCheckerByHR());
        }

        // Si se llenaron todos los campos guardon la accion.
        if (StringUtils.isNotEmpty(getNewAction())) {
            // Agrego la accion.
            Action action = new Action();
            action.setName(StringEscapeUtils.escapeHtml(getNewAction()));
            getModel().getActionsToDo().add(action);

        }

        // Guardo la meeting en la session.
        getSession().put("meeting", getModel());

        // Seteo los campos del formulario.
        setFormThemesAndActions();

        return SUCCESS;
    }

    /**
     * Elimina un action de la meeting
     * 
     * @return
     */
    public String deleteAction() {
        // Obtengo la Meeting por la sesion.
        Meeting meet = (Meeting) getSession().get("meeting");
        setModel(meet);

        // Obtengo la accion y la elimino de la lista.
        Action actionForDelete = (Action) CollectionUtils.find(getModel().getActionsToDo(),
                new BeanPropertyValueEqualsPredicate("name", getSelectedActionForDelete()));
        getModel().getActionsToDo().remove(actionForDelete);

        // Guardo la fecha
        if (getSelectedMeetingDate() != null) {
            getModel().setDate(getCompleteDate(getSelectedMeetingDate()));
        }
        // Guardo la opcion CheckedByHR
        if (getCheckerByHR() != null) {
            getModel().setCheckedByHR(getCheckerByHR());
        }

        // Guardo la meeting en la session.
        getSession().put("meeting", getModel());

        // Seteo los campos del formulario.
        setFormThemesAndActions();

        return SUCCESS;
    }

    /*
     * Limpia los campos de temas y acciones
     */
    private void setFormThemesAndActions() {
        // Seteo los campos del formulario.
        setProperties(getModel());
        setNewAction("");
        setNewTopic("");
        setSelectedPriorityType("");
        setSelectedTopicType("");

        if (getSession().containsKey("editMode") && (Boolean) getSession().get("editMode")) {
            setMappedRequest("editMeeting");
        } else {
            setMappedRequest("listPerformedMeeting");
        }
    }

    /**
     * Devuelve si la seleccion de mentor y mentorado es requerida
     * 
     * @return
     */
    public Boolean getAreEmployeesRequired() {
        if (getActionMethod() != null && getActionMethod().equals("MeetingAction_list"))
            return Boolean.FALSE;
        return Boolean.TRUE;
    }

    /*
     * VALIDATIONS
     */

    /**
     * Valida si el mentor y mentorado indicados corresponden a una relacion
     * vigente.
     */
    private boolean actualMentoringRelationship() {
        Employee mentee = employeeService.searchById(Long.parseLong(getSelectedEmployeeId()));
        Employee mentor = employeeService.searchById(Long.parseLong(getSelectedMentorId()));

        if (!mentee.getMentor().getId().equals(mentor.getId())) {
            addFieldError("mentor and mentee", mentor.getPerson().getName() + " is not the mentor of "
                    + mentee.getPerson().getName() + ". Please verify the parameters");
            return false;
        }
        return true;
    }

    /**
     * Valida los campos ingresados para poder guardar la nueva meeting sin
     * error
     */
    public String validateAddNewMeeting() {
        if (getValidated()) {// already validated and accepted the message
            setMappedRequest("addNewMeeting");
            return SUCCESS;
        }

        Boolean error = Boolean.FALSE;

        // Compruebo si se lleno el campo de Mentor y mentorado.
        if (validatePersons()) {
            setModel(null);
            setMappedRequest("addMeeting");
            return SUCCESS;
        }

        // Compruebo que pertenesca a una relaci�n mentor-mentorado actual.
        if (!actualMentoringRelationship()) {
            setModel(null);
            setMappedRequest("addMeeting");
            return SUCCESS;
        }

        // Compruebo la fecha completa
        error = error || validateCompleteDate(getNextMeetingDate(), Boolean.FALSE);

        if (error) {
            setMappedRequest("addMeeting");
            return ERROR;
        }

        // Comprobar periodo maximo de tiempo aceptable
        Meeting selectedMeeting = new Meeting();
        selectedMeeting.setEmployee(employeeService.searchById(NumberUtils.toLong(getSelectedEmployeeId(),
                Long.MIN_VALUE)));
        selectedMeeting
                .setMentor(employeeService.searchById(NumberUtils.toLong(getSelectedMentorId(), Long.MIN_VALUE)));
        selectedMeeting.setDate(getNextMeetingDate());

        error = validateMaximumPeriodAcceptable(selectedMeeting);

        if (getMaxTimeNextMeetingError() || getMaxActualTimeError()) {
            setMappedRequest("addMeeting");
            return ERROR;
        }

        setMappedRequest("addNewMeeting");
        return SUCCESS;
    }

    /*
     * Valida que los campos Mentee y Mentor esten seleccionados
     */
    private Boolean validatePersons() {
        Boolean error = Boolean.FALSE;

        if (StringUtils.isEmpty(selectedMentorId) || !StringUtils.isNumeric(selectedMentorId)) {
            addFieldError("mentor", "Mentor is required");
            error = Boolean.TRUE;
        }
        if (StringUtils.isEmpty(selectedEmployeeId) || !StringUtils.isNumeric(selectedEmployeeId)) {
            addFieldError("mentee", "Mentee is required");
            error = Boolean.TRUE;
        }

        return error;
    }

    /*
     * Valida si el per�odo de fechas supera el m�ximo aceptable
     */
    private Boolean validateMaximumPeriodAcceptable(final Meeting selectedMeeting) {
        Boolean error = Boolean.FALSE;

        Meeting lastMeeting = meetingService.findPreviousMeeting(selectedMeeting);
        if (lastMeeting != null) {

            int daysBetween = DateUtil.getWorkingDaysBetween(lastMeeting.getDate(), selectedMeeting.getDate(),
                    getHolidaysFromService(lastMeeting.getDate(), selectedMeeting.getDate())) - 1;
            if (daysBetween > getMaxPeriodBetweenMeetings()) {
                Calendar c = new GregorianCalendar();
                c.setTime(lastMeeting.getDate());

                // si el dia es mayor a 28, lo convierto en 28 para evitar
                // errores del tipo: 30/02 no es fecha valida.
                if (c.get(Calendar.DAY_OF_MONTH) > 28) {
                    c.set(Calendar.DAY_OF_MONTH, 28);
                }
                c.add(Calendar.MONTH, 4);

                Date nextMeetingCalculated = DateUtil.calculateNextDate(lastMeeting.getDate(),
                        getMaxPeriodBetweenMeetings() + 1, getHolidaysFromService(lastMeeting.getDate(), c.getTime()));

                if (nextMeetingCalculated.after(DateUtil.getCurrentDate())) {
                    setMaxTimeNextMeetingError(Boolean.TRUE);
                    error = Boolean.TRUE;
                } else {
                    setMaxActualTimeError(Boolean.TRUE);
                    error = Boolean.TRUE;
                }
                addActionError("Maximum period acceptable");
                setRecommendedDate(DateUtil.getFechaFormateada(nextMeetingCalculated));
            }
        }

        return error;
    }

    /**
     * Valida los campos ingresados para poder guardar la meeting editada sin
     * error
     */
    public String validateSaveEditedMeeting() {
        if (getValidated()) {// already validated and accepted the message
            setMappedRequest("saveEditedMeeting");
            return SUCCESS;
        }

        Boolean error = Boolean.FALSE;

        // Compruebo la fecha completa
        Boolean notFuture = null;
        if (MeetingStatus.PERFORMED.equals(getModel().getStatus())) {
            notFuture = Boolean.TRUE;
        }
        error = error || validateCompleteDate(getSelectedMeetingDate(), notFuture);

        Meeting meet = (Meeting) getSession().get("meeting");
        setModel(meet);

        // Si la meeting esta en Performed y no se carg� ning�n tema a la
        // meeting cancelo la confirmaci�n y muestro un mensaje de error.
        if (MeetingStatus.PERFORMED.equals(getModel().getStatus())
                && (getModel().getNoteThemes() == null || getModel().getNoteThemes().isEmpty())) {
            addFieldError("noThemeError", "It's required to add at least one theme to the meeting to be saved.");
            error = Boolean.TRUE;
        }

        if (error) {
            setMappedRequest("editMeeting");
            return ERROR;
        }

        Meeting selectedMeeting = getModel();
        selectedMeeting.setDate(getCompleteDate(getSelectedMeetingDate()));
        selectedMeeting.setCheckedByHR(getCheckerByHR());

        // Comprobar periodo maximo de tiempo aceptable
        error = error || validateMaximumPeriodAcceptable(selectedMeeting);

        if (getMaxTimeNextMeetingError() || getMaxActualTimeError()) {
            setMappedRequest("editMeeting");
            return ERROR;
        }

        setMappedRequest("saveEditedMeeting");
        return SUCCESS;
    }

    /**
     * Valida los campos ingresados para poder confirmar la meeting
     */
    public String validateConfirmPerformedMeeting() {
        if (getValidated()) {// already validated and accepted the message
            setMappedRequest("confirmPerformedMeeting");
            return SUCCESS;
        }

        Boolean error = Boolean.FALSE;

        // Comprobar fecha completa
        error = error || validateCompleteDate(getSelectedMeetingDate(), Boolean.TRUE);

        Meeting meet = (Meeting) getSession().get("meeting");
        setModel(meet);

        // Si no se carg� ning�n tema a la meeting cancelo la confirmaci�n y
        // muestro un mensaje de error.
        if (getModel().getNoteThemes() == null || getModel().getNoteThemes().isEmpty()) {
            addFieldError("noThemeError", "It's required to add at least one theme to the meeting to be saved.");
            error = Boolean.TRUE;
        }

        if (error) {

            // Guardo la meeting en la session.
            getSession().put("meeting", getModel());

            // Seteo los campos del formulario.
            setFormThemesAndActions();

            setMappedRequest("listPerformedMeeting");
            return ERROR;
        }

        setMappedRequest("confirmPerformedMeeting");
        return SUCCESS;
    }

    /**
     * Valida los campos ingresados para poder buscar la meeting
     */
    public String validateListPerformedMeeting() {
        if (getValidated()) {// already validated and accepted the message
            setMappedRequest("listPerformedMeeting");
            return SUCCESS;
        }

        if (validatePersons()) {
            setModel(null);
            setMappedRequest("searchPerformedMeeting");
            return SUCCESS;
        }

        setMappedRequest("listPerformedMeeting");
        return SUCCESS;
    }

    /**
     * Valida los campos ingresados para poder buscar meetings
     */
    private void validateList() {

        // La fecha desde no puede ser mayor que la fecha hasta
        if (getMeetingDateFrom() != null && getMeetingDateTo() != null
                && getMeetingDateFrom().after(getMeetingDateTo())) {
            addFieldError("datesError", "Verify the date period entered since it's incorrect.");
        }

    }

    private Boolean validateCompleteDate(final Date meetingDateToValidate, final Boolean notFuture) {
        Boolean error = Boolean.FALSE;

        // Comprobar fecha seleccionada
        if (meetingDateToValidate == null) {
            addFieldError("date", "Date is required");
            error = Boolean.TRUE;
        }

        // Comprobar que hora y minutos est�n completos
        if (getSelectedHour() == null) {
            addFieldError("hour", "Hour is required");
            error = Boolean.TRUE;
        }
        if (getSelectedMinutes() == null) {
            addFieldError("minutes", "Minutes are required");
            error = Boolean.TRUE;
        }
        if (getSelectedMeridian() == null) {
            addFieldError("meridian", "Meridian is required");
            error = Boolean.TRUE;
        }

        if (notFuture != null) {
            // Compruebo que la fecha no sea anterior al dia actual
            if (Boolean.FALSE.equals(notFuture)) {
                if (meetingDateToValidate != null && meetingDateToValidate.before(DateUtil.getCurrentDate())) {
                    addFieldError("date", "Meeting Date must be greater or equal than the current date");
                    error = Boolean.TRUE;
                }
            } else {
                // Comprobar que la fecha no sea futura
                if (meetingDateToValidate != null && meetingDateToValidate.after(DateUtil.getCurrentDate())) {
                    addFieldError("date", "Meeting Date cannot be greater than the current date.");
                    error = Boolean.TRUE;
                }
            }
        }

        // Comprobar que no exista otra meeting para la misma fecha ni en el
        // mismo momento del d�a
        if (meetingDateToValidate != null && getSelectedHour() != null && getSelectedMinutes() != null
                && getSelectedMeridian() != null) {
            Meeting searched = null;
            if (getSession().get("meeting") != null) {
                searched = (Meeting) getSession().get("meeting");
            } else {
                searched = getModel();
                searched.setEmployee(employeeService.searchById(NumberUtils.toLong(getSelectedEmployeeId(),
                        Long.MIN_VALUE)));
                searched.setMentor(employeeService.searchById(NumberUtils.toLong(getSelectedMentorId(), Long.MIN_VALUE)));
            }

            searched.setDate(getCompleteDate(meetingDateToValidate));// meetingService.searchByCriteria(searched)
            Collection<Meeting> meetings = meetingService.searchDuplicatedMeetings(searched);
            if (!CollectionUtils.isEmpty(meetings)) {
                addFieldError("date", "Meeting Date cannot be equal than another meeting.");
                error = Boolean.TRUE;
            }
        }
        return error;
    }
}